#ifndef REALGEN_CU
#define REALGEN_CU

// Includes
#include <iostream>

#include "realGen.cuh"

#include "../utils/cuPrintf.cu"

//Incluyo la clase Random para la generación de números aleatorios en CUDA
#include "../utils/Random.cu"

//Incluyo la clase global.cu  que contiene las variables y funciones globales
#include "../utils/global.cu"

//Incluyo la clase realCPU.cu para procesar las islas en CPU
#include "realCPU.cuh"

//Incluyo la clase donde se calcula el fitness
#include "../fitness/fitnessReal.cu"

//Incluyo la clase donde se inicializa la poblacion manualmente
#include "../inicializacion/initRealGen.cu"
#include "../inicializacion/initRealCPU.cu"

using namespace std;


/****************** Representacion real ********************/

/******************* Implementacion de Funciones del Device *****************/

__global__ void inicializarPoblacionRealGen(float* poblacion, float* fitnessActualDevice, struct Random randomGPU, int cantCromosomasPoblacion, float* fitnessOrdenadoDevice, float* numerosAleatoriosInicializacion){
	int posCromosoma = blockIdx.y * cantGenesEnCromosomaDevice * 2;
	int posCromosoma2 = posCromosoma + cantGenesEnCromosomaDevice;
	for(int i=threadIdx.y; i<cantGenesEnCromosomaDevice; i=i+cantHilosBloqueDevice){
		//Obtengo posiciones a escribir.
		int posicionGen = posCromosoma + i;
		int posicionGen2 = posCromosoma2 + i;
		//Genero valor aleatorio.
		poblacion[posicionGen] = obtenerNumeroEnRangoReal(numerosAleatoriosInicializacion[posicionGen]);
		poblacion[posicionGen2] = obtenerNumeroEnRangoReal(numerosAleatoriosInicializacion[posicionGen2]);
	}
}

__global__ void mainKernelRealGen(float* poblacionActualDevice, float* nuevaPoblacionDevice, float* fitnessActualDevice, float* nuevoFitnessDevice, int* poblacionActualAcambiarDevice, int*  nuevaPoblacionAcambiarDevice, float* individuosAMigrarDevice, float * probabilidadRuletaDevice, float * fitnessTotalParaRuletaDevice, struct Random r, int generacionActual, float * individuoAMigrarGPUaCPUdevice, float* fitnessOrdenadoDevice, int* vectorIndicesFitnessOrdenado, unsigned long* numerosAleatoriosSeleccion, unsigned long* numerosAleatoriosCruzamiento, unsigned long* numerosAleatoriosMutacion, unsigned long* numerosAleatoriosProbabilidadCruzamiento, unsigned long* numerosAleatoriosProbabilidadMutacion){
	int posicionCromosoma1 = seleccionRealGen(fitnessActualDevice, probabilidadRuletaDevice, r, tipoSeleccionDevice, vectorIndicesFitnessOrdenado, numerosAleatoriosSeleccion, 0);
	int posicionCromosoma2 = seleccionRealGen(fitnessActualDevice, probabilidadRuletaDevice, r, tipoSeleccionDevice, vectorIndicesFitnessOrdenado, numerosAleatoriosSeleccion, 1);
	cruzamientoRepresentacionRealGen(poblacionActualDevice, nuevaPoblacionDevice, posicionCromosoma1, posicionCromosoma2, probabilidadCruzamientoDevice, r, tipoCruzamientoDevice, numerosAleatoriosProbabilidadCruzamiento, numerosAleatoriosCruzamiento);
	mutacionRealGen(nuevaPoblacionDevice, probabilidadMutacionDevice, r, tipoMutacionDevice, generacionActual, numerosAleatoriosProbabilidadMutacion, numerosAleatoriosMutacion);
}

__global__ void asignarProbabilidadRuletaRealGen(float* fitnessActualDevice, float* probabilidadRuletaReal, float* fitnessTotalParaRuletaDevice, struct Random randomGPU){
	__syncthreads();
	if(((blockIdx.y * 2) % cantCromosomasPoblacionDevice == 0) && (threadIdx.y == 0)){
		fitnessTotalParaRuletaDevice[blockIdx.y*2/cantCromosomasPoblacionDevice] = 0;
		for(int i = blockIdx.y*2; i < blockIdx.y*2 + cantCromosomasPoblacionDevice; i++){
			fitnessTotalParaRuletaDevice[blockIdx.y*2/cantCromosomasPoblacionDevice] += fitnessActualDevice[i];
		}
	}
	if((blockIdx.y * 2) % cantCromosomasPoblacionDevice == 0 && threadIdx.y == 0){
		for(int i = blockIdx.y*2; i < blockIdx.y*2 + cantCromosomasPoblacionDevice; i++){
			probabilidadRuletaReal[i] = fitnessActualDevice[i]/fitnessTotalParaRuletaDevice[blockIdx.y*2/cantCromosomasPoblacionDevice] * 100;
		}
	}
}


__global__ void obtenerMejoresIndividuoRealGen(float* poblacion, float* fitness, float* mejorIndividuoDevice, float* mejorFitnessIndividuoGPUDevice){
	//obtengo el mejor individuo
	int posMejor = obtenerMejorIndividuoRealGen(fitness);
	for(int i = 0; i < cantGenesEnCromosomaDevice; i++){
		mejorIndividuoDevice[i + blockIdx.y * cantGenesEnCromosomaDevice] = poblacion[posMejor+i];
	}
	//asignar mejor fitness
	mejorFitnessIndividuoGPUDevice[blockIdx.y] = fitness[posMejor/cantGenesEnCromosomaDevice];
}

/******************* Implementacion de Funciones de Migracion *****************/

//Obtiene el mejor individuo de una poblacion (1 bloque por poblacion)
//selecciona el mejor elemento
__device__ int obtenerMejorIndividuoRealGen(float* vectorFitness){
	float mejor = INT_MIN;
	int indiceMejor;
	float valorFitness;
	int indiceMemoria = blockIdx.y * cantCromosomasPoblacionDevice;
	for(int i = 0; i< cantCromosomasPoblacionDevice; i++){
		valorFitness = vectorFitness[indiceMemoria+i];
		if(valorFitness > mejor){
			mejor = valorFitness;
			indiceMejor = i;
		}
	}
	int posicionCromosomaMejor = blockIdx.y * cantCromosomasPoblacionDevice * cantGenesEnCromosomaDevice + indiceMejor * cantGenesEnCromosomaDevice;
	return posicionCromosomaMejor;
}

//Obtiene el indice del peor individuo y del fitness del peor individuo de una poblacion (1 bloque por poblacion) y retorna por referencia en el arreglo indices
__device__ void obtenerPeorIndividuoRealGen(float* vectorFitness, int indices[2]){
	float peor = INT_MAX;
	int indicePeor;
	float valorFitness;
	int posicionCromosomaPeor;
	int indiceMemoria = blockIdx.y * cantCromosomasPoblacionDevice;
	for(int i = 0; i< cantCromosomasPoblacionDevice; i++){
		valorFitness = vectorFitness[indiceMemoria+i];
		if(valorFitness < peor){
			peor = valorFitness;
			indicePeor = i;
		}
	}
	posicionCromosomaPeor = blockIdx.y * cantCromosomasPoblacionDevice * cantGenesEnCromosomaDevice + indicePeor * cantGenesEnCromosomaDevice;
	indices[0] = posicionCromosomaPeor;
	indices[1] = indicePeor;
}

__global__ void migracionRepresentacionRealGen(float* poblacionActualDevice, float* fitnessActualDevice, float* individuosAMigrarDevice, struct Random randomGPU, float* individuoAMigrarCPUaGPUdevice){
	int indices[2];
	obtenerPeorIndividuoRealGen(fitnessActualDevice, indices);
	int posicionCromosomaEmigrante = indices[0];

	int posicionCromosomaInmigrante;
	int aux;

	if(blockIdx.y == 0){
		aux = cantidadPoblacionesGPUDevice - 1;
	}else{
		aux = (blockIdx.y - 1) % cantidadPoblacionesGPUDevice;
	}

	//cuPrintf("blockIdx.y - 1 = %d \n", blockIdx.y - 1);
	//cuPrintf("aux = %d \n", aux);

	posicionCromosomaInmigrante = aux * cantGenesEnCromosomaDevice;

	if(blockIdx.y == 0 && cantidadPoblacionesCPUDevice > 0){
		for(int i = 0; i<cantGenesEnCromosomaDevice; i++){
			poblacionActualDevice[posicionCromosomaEmigrante+i] = individuoAMigrarCPUaGPUdevice[i];
		}
	}else{
		for(int i = 0; i<cantGenesEnCromosomaDevice; i++){
			poblacionActualDevice[posicionCromosomaEmigrante+i] = individuosAMigrarDevice[posicionCromosomaInmigrante+i];
		}
	}

	int posicionFitness = blockIdx.y * cantCromosomasPoblacionDevice + indices[1];
	fitnessActualDevice[posicionFitness] = calcularFitnessRealGPU(poblacionActualDevice, posicionCromosomaEmigrante);

	//cuPrintf("posicionCromosomaInmigrante = %d \n", posicionCromosomaInmigrante);
	//cuPrintf("posicionCromosomaEmigrante = %d \n", posicionCromosomaEmigrante);
	//cuPrintf("posicionFitness = %d \n", posicionFitness);
}

__global__ void individosAMigrarRepresentacionRealGen(float* nuevoFitnessDevice, float* nuevaPoblacionDevice, float* individuosAMigrarDevice, float* individuoAMigrarGPUaCPUdevice){
	//int posicionMejorIndividuo = seleccionPorRangoRealGen(nuevoFitnessDevice, 1);
	int posicionMejorIndividuo = obtenerMejorIndividuoRealGen(nuevoFitnessDevice);
	//if(cantidadPoblacionesCPUDevice > 0 && blockIdx.y == cantidadPoblacionesGPUDevice*cantCromosomasPoblacionDevice/2 - cantCromosomasPoblacionDevice/2){
	if(cantidadPoblacionesCPUDevice > 0 && blockIdx.y == cantidadPoblacionesGPUDevice - 1){
		for(int i = 0; i<cantGenesEnCromosomaDevice; i++){
			individuoAMigrarGPUaCPUdevice[i] = nuevaPoblacionDevice[posicionMejorIndividuo+i];
		}
	}else{
		for(int i = 0; i<cantGenesEnCromosomaDevice; i++){
			individuosAMigrarDevice[blockIdx.y*cantGenesEnCromosomaDevice + i] = nuevaPoblacionDevice[posicionMejorIndividuo+i];
		}
	}
}


/******************* Implementacion de Funciones de Selecccion *****************/

__device__ int seleccionRealGen(float* vectorFitness, float* probabilidadRuletaDevice, struct Random randomGPU, int ejecutarFuncion, int* vectorIndicesFitnessOrdenado, unsigned long* numerosAleatoriosSeleccion, int nroIndividuo){
	int resultado = -1;
	if(ejecutarFuncion == 0){
		resultado = seleccionPorRangoRealGen(vectorIndicesFitnessOrdenado, randomGPU, numerosAleatoriosSeleccion, nroIndividuo);
	}else{
		if(ejecutarFuncion == 1){
			resultado = seleccionPorTorneoRealGen(vectorFitness, randomGPU, numerosAleatoriosSeleccion, nroIndividuo);
		}else if(ejecutarFuncion == 2){
			resultado = seleccionRuedaRuletaRealGen(probabilidadRuletaDevice, randomGPU, numerosAleatoriosSeleccion, nroIndividuo);
		}
	}
	return resultado;
}


//Seleccion elitista
//selecciona el mejor o segundo mejor elemento segun se indica en indiceMejor con un 1 o con un 2
__device__ int seleccionPorRangoRealGen(int* vectorIndicesFitnessOrdenado, struct Random randomGPU, unsigned long* numerosAleatoriosSeleccion, int nroIndividuo){
	//cuPrintf("*** Seleccion elitista real ***\n");
	int indice =  blockIdx.y * 2 + nroIndividuo;
	unsigned int indiceMejor = numerosAleatoriosSeleccion[indice] % cantidadIndividuosSeleccionPorRangoDevice;
	int indiceFitness = (blockIdx.y / (cantCromosomasPoblacionDevice / 2)) * cantCromosomasPoblacionDevice + indiceMejor;

	int inicioPoblacion = (blockIdx.y / (cantCromosomasPoblacionDevice / 2)) * cantCromosomasPoblacionDevice * cantGenesEnCromosomaDevice;
	//cuPrintf("indMejor = %d, iniPob = %d, vector[indiceFitness] = %d, vector[indiceFitness] * cantGenesEnCromosomaDevice = %d\n", indiceMejor, inicioPoblacion, vectorIndicesFitnessOrdenado[indiceFitness], vectorIndicesFitnessOrdenado[indiceFitness] * cantGenesEnCromosomaDevice);
	//cuPrintf("indiceMejor = %d\n", indiceMejor);
	//cuPrintf("vectorIndicesFitnessOrdenado[%d] = %d\n", indiceFitness, vectorIndicesFitnessOrdenado[indiceFitness]);
	int posicionCromosoma = inicioPoblacion + vectorIndicesFitnessOrdenado[indiceFitness] * cantGenesEnCromosomaDevice;
	return posicionCromosoma;	
}

//Seleccion por torneo
//selecciona 2 individuos al azar y devuelve la posicion del mejor
__device__ int seleccionPorTorneoRealGen(float* vectorFitness, struct Random randomGPU, unsigned long* numerosAleatoriosSeleccion, int nroIndividuo){
	//cuPrintf("*** Seleccion por torneo real ***\n");
	
	int indice =  blockIdx.y * 4 + nroIndividuo * 2;
	unsigned int r = numerosAleatoriosSeleccion[indice] % cantCromosomasPoblacionDevice;
	int posicionFitnessAleatoria1 = (blockIdx.y * 2 / cantCromosomasPoblacionDevice) * cantCromosomasPoblacionDevice + r;
	int posicionCromosomaAleatoria1 = (blockIdx.y * 2 / cantCromosomasPoblacionDevice) * cantCromosomasPoblacionDevice * cantGenesEnCromosomaDevice + r * cantGenesEnCromosomaDevice;
	
	r =  numerosAleatoriosSeleccion[indice+1] % cantCromosomasPoblacionDevice;
	int posicionFitnessAleatoria2 = (blockIdx.y * 2 / cantCromosomasPoblacionDevice) * cantCromosomasPoblacionDevice + r;
	int posicionCromosomaAleatoria2 = (blockIdx.y * 2 / cantCromosomasPoblacionDevice) * cantCromosomasPoblacionDevice * cantGenesEnCromosomaDevice + r * cantGenesEnCromosomaDevice;
	
	if(vectorFitness[posicionFitnessAleatoria1] > vectorFitness[posicionFitnessAleatoria2])
		return posicionCromosomaAleatoria1;
	return posicionCromosomaAleatoria2;
	
}

//Seleccion por rueda de ruleta
__device__ int seleccionRuedaRuletaRealGen(float* probabilidadRuletaDevice, struct Random randomGPU, unsigned long* numerosAleatoriosSeleccion, int nroIndividuo){
	int indice =  blockIdx.y * 2 + nroIndividuo;
	//cuPrintf("indice = %d, nroIndividuo = %d, valor = %10.8lu\n", indice, nroIndividuo, numerosAleatoriosSeleccion[indice]);
	float aux = numerosAleatoriosSeleccion[indice];
	float r = fmod(aux, 100);
	//cuPrintf("r, %f\n", r);
	float suma = probabilidadRuletaDevice[(blockIdx.y / (cantCromosomasPoblacionDevice / 2)) * cantCromosomasPoblacionDevice];
	//cuPrintf("suma %f\n", suma);
	int i = 0;
	while((r > suma) && (i < cantCromosomasPoblacionDevice - 1)){
		i++;
		suma = suma + probabilidadRuletaDevice[(blockIdx.y / (cantCromosomasPoblacionDevice / 2)) * cantCromosomasPoblacionDevice + i];
	}
	//cuPrintf("i %d\n", i);
	//return blockIdx.y * cantGenesEnCromosomaDevice * 2 + i * cantGenesEnCromosomaDevice;
	return (blockIdx.y / (cantCromosomasPoblacionDevice / 2)) * cantCromosomasPoblacionDevice * cantGenesEnCromosomaDevice + i * cantGenesEnCromosomaDevice;
}


/******************* Implementacion de Funciones de Cruzamiento *****************/

__device__ void cruzamientoRepresentacionRealGen(float* poblacionActual, float* nuevaPoblacion, int posicionCromosoma1, int posicionCromosoma2, float probabilidad, struct Random randomGPU, int ejecutarFuncion, unsigned long* numerosAleatoriosProbabilidadCruzamiento, unsigned long* numerosAleatoriosCruzamiento){
	if(ejecutarFuncion == 0){
		cruzamientoAritmeticoIndividualGen(poblacionActual, nuevaPoblacion, posicionCromosoma1, posicionCromosoma2, probabilidad, randomGPU, numerosAleatoriosProbabilidadCruzamiento, numerosAleatoriosCruzamiento);
	}else{
		if(ejecutarFuncion == 1){
			cruzamientoAritmeticoSimpleGen(poblacionActual, nuevaPoblacion, posicionCromosoma1, posicionCromosoma2, probabilidad, randomGPU, numerosAleatoriosProbabilidadCruzamiento, numerosAleatoriosCruzamiento);
		}else{
			if(ejecutarFuncion == 2){
				cruzamientoAritmeticoCompletoGen(poblacionActual, nuevaPoblacion, posicionCromosoma1, posicionCromosoma2, probabilidad, randomGPU, numerosAleatoriosProbabilidadCruzamiento, numerosAleatoriosCruzamiento);
			}
		}
	}
}

__device__ void cruzamientoAritmeticoIndividualGen(float* poblacionActual, float* nuevaPoblacion, int posicionCromosoma1, int posicionCromosoma2, float probabilidad, struct Random randomGPU, unsigned long* numerosAleatoriosProbabilidadCruzamiento, unsigned long* numerosAleatoriosCruzamiento){
	int posicionNuevoGen = blockIdx.y * cantGenesEnCromosomaDevice * 2;
	int posicionNuevoGen2 = posicionNuevoGen + cantGenesEnCromosomaDevice;
	int indiceProb = blockIdx.y;
	int indiceCruz = blockIdx.y * 2;
	unsigned int prob = numerosAleatoriosProbabilidadCruzamiento[indiceProb] % 101;
	if(prob < probabilidad){
		int k = numerosAleatoriosCruzamiento[indiceCruz] % cantGenesEnCromosomaDevice;
		float alfa = fmod(numerosAleatoriosCruzamiento[indiceCruz + 1] / 1000.9762, 1);
		for(int i = threadIdx.y; i < cantGenesEnCromosomaDevice; i = i + cantHilosBloqueDevice){
			if(i == k){
				nuevaPoblacion[posicionNuevoGen+i] = alfa * poblacionActual[posicionCromosoma2+i] + (1-alfa) * poblacionActual[posicionCromosoma1+i];
				nuevaPoblacion[posicionNuevoGen2+i] = alfa * poblacionActual[posicionCromosoma1+i] + (1-alfa) * poblacionActual[posicionCromosoma2+i];
			}else{
				nuevaPoblacion[posicionNuevoGen+i] = poblacionActual[posicionCromosoma1+i];
				nuevaPoblacion[posicionNuevoGen2+i] = poblacionActual[posicionCromosoma2+i];
			}
		}
	}else{
		for(int i = threadIdx.y; i < cantGenesEnCromosomaDevice; i = i + cantHilosBloqueDevice){
			nuevaPoblacion[posicionNuevoGen+i] = poblacionActual[posicionCromosoma1+i];
			nuevaPoblacion[posicionNuevoGen2+i] = poblacionActual[posicionCromosoma2+i];
		}
	}
}

__device__ void cruzamientoAritmeticoSimpleGen(float* poblacionActual, float* nuevaPoblacion, int posicionCromosoma1, int posicionCromosoma2, float probabilidad, struct Random randomGPU, unsigned long* numerosAleatoriosProbabilidadCruzamiento, unsigned long* numerosAleatoriosCruzamiento){
	int posicionNuevoGen = blockIdx.y * cantGenesEnCromosomaDevice * 2;
	int posicionNuevoGen2 = posicionNuevoGen + cantGenesEnCromosomaDevice;
	int indiceProb = blockIdx.y;
	int indiceCruz = blockIdx.y * 2;
	unsigned int prob = numerosAleatoriosProbabilidadCruzamiento[indiceProb] % 101;
	if(prob < probabilidad){
		int k = numerosAleatoriosCruzamiento[indiceCruz] % cantGenesEnCromosomaDevice;
		float alfa = fmod(numerosAleatoriosCruzamiento[indiceCruz + 1] / 1000.9762, 1);
		for(int i = threadIdx.y; i < cantGenesEnCromosomaDevice; i = i + cantHilosBloqueDevice){
			if(i >= k){
				nuevaPoblacion[posicionNuevoGen+i] = alfa * poblacionActual[posicionCromosoma2+i] + (1-alfa) * poblacionActual[posicionCromosoma1+i];
				nuevaPoblacion[posicionNuevoGen2+i] = alfa * poblacionActual[posicionCromosoma1+i] + (1-alfa) * poblacionActual[posicionCromosoma2+i];
			} else{
				nuevaPoblacion[posicionNuevoGen+i] = poblacionActual[posicionCromosoma1+i];
				nuevaPoblacion[posicionNuevoGen2+i] = poblacionActual[posicionCromosoma2+i];
			}

		}
	}else{
		for(int i = threadIdx.y; i < cantGenesEnCromosomaDevice; i = i + cantHilosBloqueDevice){
			nuevaPoblacion[posicionNuevoGen+i] = poblacionActual[posicionCromosoma1+i];
			nuevaPoblacion[posicionNuevoGen2+i] = poblacionActual[posicionCromosoma2+i];
		}
	}
}

__device__ void cruzamientoAritmeticoCompletoGen(float* poblacionActual, float* nuevaPoblacion, int posicionCromosoma1, int posicionCromosoma2, float probabilidad, struct Random randomGPU, unsigned long* numerosAleatoriosProbabilidadCruzamiento, unsigned long* numerosAleatoriosCruzamiento){
	int posicionNuevoGen = blockIdx.y * cantGenesEnCromosomaDevice * 2;
	int posicionNuevoGen2 = posicionNuevoGen + cantGenesEnCromosomaDevice;
	int indice = blockIdx.y;
	unsigned int prob = numerosAleatoriosProbabilidadCruzamiento[indice] % 101;
	if(prob < probabilidad){
		float alfa = fmod(numerosAleatoriosCruzamiento[indice] / 1000.9762, 1);
		for(int i = threadIdx.y; i < cantGenesEnCromosomaDevice; i = i + cantHilosBloqueDevice){
			nuevaPoblacion[posicionNuevoGen+i] = alfa * poblacionActual[posicionCromosoma1+i] + (1-alfa) * poblacionActual[posicionCromosoma2+i];
			nuevaPoblacion[posicionNuevoGen2+i] = alfa * poblacionActual[posicionCromosoma2+i] + (1-alfa) * poblacionActual[posicionCromosoma1+i];
		}
	}else{
		for(int i = threadIdx.y; i < cantGenesEnCromosomaDevice; i = i + cantHilosBloqueDevice){
			nuevaPoblacion[posicionNuevoGen+i] = poblacionActual[posicionCromosoma1+i];
			nuevaPoblacion[posicionNuevoGen2+i] = poblacionActual[posicionCromosoma2+i];
		}
	}
}


/******************* Implementacion de Funciones de Mutacion *****************/


__device__ void mutacionRealGen(float* poblacion, float probabilidad, struct Random randomGPU, int ejecutarFuncion, int generacionActual, unsigned long* numerosAleatoriosProbabilidadMutacion, unsigned long* numerosAleatoriosMutacion){
	if(ejecutarFuncion == 0){
		mutacionUniformeGen(poblacion, probabilidad, randomGPU, numerosAleatoriosProbabilidadMutacion, numerosAleatoriosMutacion);
	}else{
		if(ejecutarFuncion == 1){
			mutacionNoUniformeGen(poblacion, probabilidad, randomGPU, generacionActual, numerosAleatoriosProbabilidadMutacion, numerosAleatoriosMutacion);
		}
	}
}


__device__ void mutacionUniformeGen(float* poblacion, float probabilidad, struct Random randomGPU, unsigned long* numerosAleatoriosProbabilidadMutacion, unsigned long* numerosAleatoriosMutacion){
	__syncthreads();
	if(threadIdx.y == 0){
		int posicionNuevoGen = blockIdx.y * cantGenesEnCromosomaDevice * 2;
		int posicionNuevoGen2 = posicionNuevoGen + cantGenesEnCromosomaDevice;
		unsigned int r;
		int indiceProb = blockIdx.y * 2 + threadIdx.y / cantGenesEnCromosomaDevice;
		int indiceMut = blockIdx.y * 4 + 2 * (threadIdx.y / cantGenesEnCromosomaDevice);
		unsigned int prob = (numerosAleatoriosProbabilidadMutacion[indiceProb] % 101);
		if(prob < probabilidad){
			r = (numerosAleatoriosMutacion[indiceMut] % cantGenesEnCromosomaDevice);
			float left_k, right_k, aux1;
			float aux;
			//obtengo el valor del gen de la izquierda al que se va a mutar (round robin)
			if(r == 0){
				left_k = poblacion[posicionNuevoGen + cantGenesEnCromosomaDevice - 1];
			}else{
				left_k = poblacion[posicionNuevoGen + r - 1];
			}
			//obtengo el valor del gen de la derecha al que se va a mutar (round robin)
			if(r == (cantGenesEnCromosomaDevice - 1)){
				right_k = poblacion[posicionNuevoGen - cantGenesEnCromosomaDevice + 1];
			}else{
				right_k = poblacion[posicionNuevoGen + r + 1];
			}
			aux = fmod(numerosAleatoriosMutacion[indiceMut + 1] / 1000.9762, 1);
			if(left_k > right_k){
				aux1 = right_k;
				right_k = left_k;
				left_k = aux1;
			}
			poblacion[posicionNuevoGen+r] = left_k + aux * (right_k - left_k);

		}
		//mutacion del segundo individuo con distinta probabilidad y distinto gen a mutar
		prob = numerosAleatoriosProbabilidadMutacion[indiceMut+2] % 101;
		if(prob < probabilidad){
			r = (numerosAleatoriosMutacion[indiceMut+2] % cantGenesEnCromosomaDevice);
			float left_k, right_k, aux1;
			float aux;
			//obtengo el valor del gen de la izquierda al que se va a mutar (round robin)
			if(r == 0){
				left_k = poblacion[posicionNuevoGen2 + cantGenesEnCromosomaDevice - 1];
			}else{
				left_k = poblacion[posicionNuevoGen2 + r - 1];
			}
			//obtengo el valor del gen de la derecha al que se va a mutar (round robin)
			if(r == (cantGenesEnCromosomaDevice - 1)){
				right_k = poblacion[posicionNuevoGen2 - cantGenesEnCromosomaDevice + 1];
			}else{
				right_k = poblacion[posicionNuevoGen2 + r + 1];
			}
			aux = fmod(numerosAleatoriosMutacion[indiceMut + 1] / 1000.9762, 1);
			if(left_k > right_k){
				aux1 = right_k;
				right_k = left_k;
				left_k = aux1;
			}
			poblacion[posicionNuevoGen2+r] = left_k + aux * (right_k - left_k);

		}
	}
}

__device__ void mutacionNoUniformeGen(float* poblacion, float probabilidad, struct Random randomGPU, int generacionActual, unsigned long* numerosAleatoriosProbabilidadMutacion, unsigned long* numerosAleatoriosMutacion){
	__syncthreads();
	if(threadIdx.y == 0){
		int posicionNuevoGen = blockIdx.y * cantGenesEnCromosomaDevice * 2;
		int posicionNuevoGen2 = posicionNuevoGen + cantGenesEnCromosomaDevice;
		unsigned int k;
		float r;
		int indiceProb = blockIdx.y * 2 + threadIdx.y / cantGenesEnCromosomaDevice;
		int indiceMut = blockIdx.y * 6 + 3 * (threadIdx.y / cantGenesEnCromosomaDevice);
		unsigned int prob = (numerosAleatoriosProbabilidadMutacion[indiceProb] % 101);
		if(prob < probabilidad){
			k = (numerosAleatoriosMutacion[indiceMut] % cantGenesEnCromosomaDevice);
			float left_k, right_k, resultado;
			int aux;
			r = fmod(numerosAleatoriosMutacion[indiceMut + 1] / 1000.9762, 1);
			if(k == 0){
				left_k = poblacion[posicionNuevoGen + cantGenesEnCromosomaDevice - 1];
			}else{
				left_k = poblacion[posicionNuevoGen + k - 1];
			}

			if(k == (cantGenesEnCromosomaDevice - 1)){
				right_k = poblacion[posicionNuevoGen - cantGenesEnCromosomaDevice + 1];
			}else{
				right_k = poblacion[posicionNuevoGen + k + 1];
			}
			aux = numerosAleatoriosMutacion[indiceMut + 2] % 2;
			if(aux == 0){
				resultado = poblacion[posicionNuevoGen+k] + ((right_k - poblacion[posicionNuevoGen]) * r * (__powf((1 - (generacionActual/numeroGeneracionesDevice)), gradoNoUniformidadDevice)));
			}
			else{
				resultado = poblacion[posicionNuevoGen+k] - ((poblacion[posicionNuevoGen] - left_k) * r * (__powf((1 - (generacionActual/numeroGeneracionesDevice)), gradoNoUniformidadDevice)));
			}
			poblacion[posicionNuevoGen+k] = resultado;
		}
		//mutacion del segundo individuo con distinta probabilidad y distinto gen a mutar
		prob = numerosAleatoriosProbabilidadMutacion[indiceMut+3] % 101;
		if(prob < probabilidad){
			k = (numerosAleatoriosMutacion[indiceMut + 3] % cantGenesEnCromosomaDevice);
			float left_k, right_k, resultado;
			int aux;
			r = fmod(numerosAleatoriosMutacion[indiceMut + 4] / 1000.9762, 1);
			if(k == 0){
				left_k = poblacion[posicionNuevoGen2 + cantGenesEnCromosomaDevice - 1];
			}else{
				left_k = poblacion[posicionNuevoGen2 + k - 1];
			}

			if(k == (cantGenesEnCromosomaDevice - 1)){
				right_k = poblacion[posicionNuevoGen2 - cantGenesEnCromosomaDevice + 1];
			}else{
				right_k = poblacion[posicionNuevoGen2 + k + 1];
			}
			aux = numerosAleatoriosMutacion[indiceMut + 5] % 2;
			if(aux == 0){
				resultado = poblacion[posicionNuevoGen2+k] + ((right_k - poblacion[posicionNuevoGen2]) * r * (__powf((1 - (generacionActual/numeroGeneracionesDevice)), gradoNoUniformidadDevice)));
			}
			else{
				resultado = poblacion[posicionNuevoGen2+k] - ((poblacion[posicionNuevoGen2] - left_k) * r * (__powf((1 - (generacionActual/numeroGeneracionesDevice)), gradoNoUniformidadDevice)));
			}
			poblacion[posicionNuevoGen2+k] = resultado;
		}
	}

}

/******************* Implementacion de Funciones de fitness *****************/
__global__ void asignarFitnessRealGen(float* poblacion, float* vectorFitness, float* fitnessOrdenadoDevice){
	//posicion del Primer Individuo de la Poblacion
	int posicionIndividuo = blockIdx.y * cantCromosomasPoblacionDevice * cantGenesEnCromosomaDevice;
	//fitness del primer idnividuo de la poblacion
	int posicionFitness = blockIdx.y * cantCromosomasPoblacionDevice;
	for(int i=threadIdx.y; i<cantCromosomasPoblacionDevice; i=i+cantHilosBloqueDevice){
		vectorFitness[posicionFitness+i] = calcularFitnessRealGPU(poblacion, posicionIndividuo + i * cantGenesEnCromosomaDevice);
		fitnessOrdenadoDevice[posicionFitness+i] = vectorFitness[posicionFitness+i];
	}
}


/******************** Implementacion de Funciones de reemplazo **************/
__global__ void reemplazoElitistaSeleccionIndividuosRealGen(float* poblacionActual, float* nuevaPoblacion, int* poblacionActualAcambiar, int* nuevaPoblacionAcambiar, float * fitnessActual, float* nuevoFitness, int * posIndAreemplazarViejosElitistaGPU, int * posIndAreemplazarNuevosElitistaGPU){
	int cantAremplazar = porcentajeElitismoDevice * cantCromosomasPoblacionDevice / 100;
	int peor = INT_MAX;
	int mejor = INT_MIN;
	int posPeor = -1;
	int posMejor = -1;
	int posFitness = blockIdx.y * cantCromosomasPoblacionDevice;
	//este for se encarga de inicializar los primeros cantAreemplazar valores de la estructura auxiliar poblacionActualAcambiar en 1 y guarda su peor valor para luego poder obtener los peores canAReemplazar valores
	//tambien inicializa los primeros canAreemplazar valores de nuevaPoblacionACambiar en 1 y guarda su mejor valor
	for(int i = 0; i < cantAremplazar; i++){
		if(fitnessActual[posFitness+i] < peor){
			peor = fitnessActual[posFitness+i];
			posPeor = i;
		}

		if(nuevoFitness[posFitness+i] > mejor){
			mejor = nuevoFitness[posFitness+i];
			posMejor = i;
		}

		poblacionActualAcambiar[posFitness+i] = 1;
		nuevaPoblacionAcambiar[posFitness+i] = 1;
	}

	//esta iteracion se encarga de obtener los cantAremplazar peores valores de la nueva poblacion y los mejores cantAremplazar valores de la poblacion actual
	for(int i = cantAremplazar; i < cantCromosomasPoblacionDevice; i++){
		if(fitnessActual[posFitness+i] > peor){
			poblacionActualAcambiar[posFitness+i] = 1;
			poblacionActualAcambiar[posFitness+posPeor] = 0;
			peor = INT_MAX;
			posPeor = -1;
			for(int j = 0; j <= i; j++){
				if(poblacionActualAcambiar[posFitness+j] == 1){
					if(fitnessActual[posFitness+j] < peor){
						peor = fitnessActual[posFitness+j];
						posPeor = j;
					}
				}
			}
		}else{
			poblacionActualAcambiar[posFitness+i] = 0;
		}

		if(nuevoFitness[posFitness+i] < mejor){
			nuevaPoblacionAcambiar[posFitness+i] = 1;
			nuevaPoblacionAcambiar[posFitness + posMejor] = 0;
			mejor = INT_MIN;
			posMejor = -1;
			for(int j = 0; j <= i; j++){
				if(nuevaPoblacionAcambiar[posFitness+j] == 1){
					if(nuevoFitness[posFitness+j] > mejor){
						mejor = nuevoFitness[posFitness+j];
						posMejor = j;
					}
				}
			}
		}else{
			nuevaPoblacionAcambiar[posFitness+i] = 0;
		}
	}
	for(int i = 0; i < cantAremplazar; i++){
		int j = 0;
		while(poblacionActualAcambiar[posFitness+j] == 0){
			j++;
		}

		int k = 0;
		while(nuevaPoblacionAcambiar[posFitness+k] == 0){
			k++;
		}
		//reemplazo fitness
		nuevoFitness[posFitness+k] = fitnessActual[posFitness+j];
		int posCromosomaActual = blockIdx.y * cantCromosomasPoblacionDevice * cantGenesEnCromosomaDevice + j * cantGenesEnCromosomaDevice;
		int posNuevoCromosoma = blockIdx.y * cantCromosomasPoblacionDevice * cantGenesEnCromosomaDevice + k * cantGenesEnCromosomaDevice;
		posIndAreemplazarViejosElitistaGPU[blockIdx.y * cantAremplazar + i] = posCromosomaActual;
		posIndAreemplazarNuevosElitistaGPU[blockIdx.y * cantAremplazar + i] = posNuevoCromosoma;
		poblacionActualAcambiar[posFitness+j] = 0;
		nuevaPoblacionAcambiar[posFitness+k] = 0;
	}
	for(int i = 0; i < cantAremplazar*cantidadPoblacionesGPUDevice; i++){
		cuPrintf("posPoblacionActual[%d] = %d, posPoblacionNueva[%d] = %d\n",i,posIndAreemplazarViejosElitistaGPU[i],i,posIndAreemplazarNuevosElitistaGPU[i]);
	}
}

__global__ void reemplazoRealGen(float * poblacionActualDevice, float * nuevaPoblacionDevice, int * posIndAreemplazarViejosElitistaGPU, int * posIndAreemplazarNuevosElitistaGPU){
	if(tipoReemplazoDevice == 1){
		reemplazoElitistaRealGen(poblacionActualDevice, nuevaPoblacionDevice, posIndAreemplazarViejosElitistaGPU, posIndAreemplazarNuevosElitistaGPU);
	}
}

__device__ void reemplazoElitistaRealGen(float* poblacionActual, float* nuevaPoblacion, int * posIndAreemplazarViejosElitistaGPU, int * posIndAreemplazarNuevosElitistaGPU){
	//esta iteracion se encarga de reemplazar los mejores cantAremplazar individuos de la poblacion actual por los cantAremplazar peores individuos de la nueva poblacion
	//un bloque por poblacion * cantidadAReemplazar
	//es decir se corren cantidadAReemplazar bloques por poblacion
	int posicionViejoInd = posIndAreemplazarViejosElitistaGPU[blockIdx.y];
	int posicionNuevoInd = posIndAreemplazarNuevosElitistaGPU[blockIdx.y];
	cuPrintf("posActual[%d] = %d, posNueva[%d] = %d\n",blockIdx.y,posicionViejoInd,blockIdx.y,posicionNuevoInd);
	for(int i = threadIdx.y; i < cantGenesEnCromosomaDevice; i = i + cantHilosBloqueDevice){
		nuevaPoblacion[posicionNuevoInd+i] = poblacionActual[posicionViejoInd+i];
	}
}

/******************* Implementacion de Funciones del host *****************/

void traerImprimirRealGen(float* fitnessHost, float* fitnessDevice, int tamVectorFitnessFloat, float* poblacionHost, float* poblacionDevice, int tamVectorPoblacion, int iteracion){
	cudaMemcpy(fitnessHost, fitnessDevice, tamVectorFitnessFloat, cudaMemcpyDeviceToHost);
	cudaMemcpy(poblacionHost, poblacionDevice, tamVectorPoblacion, cudaMemcpyDeviceToHost);

	cout << "\nPOBLACION ITERACION "<< iteracion<<" GPU\n";
	for(int i = 0; i<totalGenesGPU; i++){
		if(i%cantGenesEnCromosoma == 0){
			cout << "\n";

		}
		printf("%f ", poblacionHost[i]);
	}
	cout << "\n\n\nFITNESS\n";

	for(int i = 0; i<cantCromosomasPoblacion * cantidadPoblacionesGPU; i++){
		printf("%f ", fitnessHost[i]);
	}
	cout << "\n\n\n\n";
}

void traerImprimirMejorIndividuoRealGen(float* mejorFitnessIndividuoGPU, float* mejorFitnessIndividuoGPUDevice, int tamVectorFitnessMejoresIndividuosGPU, float* mejorIndividuoGPU, float* mejorIndividuoGPUDevice, int tamVectorMejoresIndividuosGPU, int iteracion){
	cudaMemcpy(mejorFitnessIndividuoGPU, mejorFitnessIndividuoGPUDevice, tamVectorFitnessMejoresIndividuosGPU, cudaMemcpyDeviceToHost);
	cudaMemcpy(mejorIndividuoGPU, mejorIndividuoGPUDevice, tamVectorMejoresIndividuosGPU, cudaMemcpyDeviceToHost);

	cout << "\nMEJOR INDIVIDUO ITERACION "<< iteracion<<" GPU\n";
	for(int i = 0; i < cantGenesEnCromosoma*cantidadPoblacionesGPU; i++){
		if(i%cantGenesEnCromosoma == 0){
			cout << "\n";
		}
		printf("%f ", mejorIndividuoGPU[i]);
	}

	cout << "\n\n\nFITNESS MEJOR INDIVIDUO GPU\n";
	for(int i = 0; i < cantidadPoblacionesGPU; i++){
		printf("%f ", mejorFitnessIndividuoGPU[i]);
	}
	cout << "\n\n\n\n";
}
void representacionRealGen(){
	int tamVectorPoblacion = sizeof(float)*totalGenesGPU;
	int tamVectorFitnessFloat = sizeof(float)*totalIndividuosGPU;
	int tamVectorFitnessInt = sizeof(int)*totalIndividuosGPU;
	int tamVectorIndividuosAMigrar = sizeof(float)*cantGenesEnCromosoma*cantidadPoblacionesGPU;
	int tamVectorFitnessRuleta = sizeof(float)*totalIndividuosGPU;
	int tamVectorPoblacionParaRuleta = sizeof(float)*cantidadPoblacionesGPU;

	int tamVectorIndividuosAMigrarGPU = sizeof(float)*cantGenesEnCromosoma*cantidadPoblacionesGPU;
	int tamVectorIndividuosAMigrarCPU = sizeof(float)*cantGenesEnCromosoma*cantidadPoblacionesCPU;
	int tamVectorPoblacionParaRuletaCPU = sizeof(float)*totalIndividuosCPU;
	int tamVectorFitnessRuletaCPU = sizeof(float)*cantidadPoblacionesCPU;
	int tamVectorMejoresIndividuosGPU = sizeof(float) * cantGenesEnCromosoma * cantidadPoblacionesGPU;
	int tamVectorMejoresIndividuosCPU = sizeof(float) * cantGenesEnCromosoma * cantidadPoblacionesCPU;
	int tamVectorFitnessMejoresIndividuosCPU = sizeof(float) * cantidadPoblacionesCPU;
	int tamVectorFitnessMejoresIndividuosGPU = sizeof(float) * cantidadPoblacionesGPU;

	static float *poblacionActualHostGPU = (float *) malloc(tamVectorPoblacion);
	static float *nuevaPoblacionHostGPU = (float *) malloc(tamVectorPoblacion);
	static float *fitnessActualHostGPU = (float *) malloc(tamVectorFitnessFloat);
	static float *nuevoFitnessHostGPU = (float *) malloc(tamVectorFitnessFloat);
	static float *probabilidadRuletaHost = (float *) malloc(tamVectorFitnessRuleta);
	static float *fitnessTotalParaRuletaHost = (float *) malloc(tamVectorPoblacionParaRuleta);
	static float *individuosAMigrarHostGPU = (float *) malloc(tamVectorIndividuosAMigrar);
	static float *mejorIndividuoCPU = (float *) malloc(tamVectorMejoresIndividuosCPU);
	static float *mejorIndividuoGPU = (float *) malloc(tamVectorMejoresIndividuosGPU);
	static float *mejorFitnessIndividuoCPU = (float *) malloc(tamVectorFitnessMejoresIndividuosCPU);
	static float *mejorFitnessIndividuoGPU = (float *) malloc(tamVectorFitnessMejoresIndividuosGPU);

//SE BORRA//
	static int *indicesFitnessOrdenadoHost = (int *) malloc(tamVectorFitnessInt);
////////////
	float *poblacionActualDevice;
	float *nuevaPoblacionDevice;
	float *fitnessActualDevice;
	float *nuevoFitnessDevice;
	float * fitnessOrdenadoDevice;
	int * indicesFitnessOrdenadoDevice;
	float *individuosAMigrarDevice;
	float *probabilidadRuletaDevice;
	float *fitnessTotalParaRuletaDevice;
	int * poblacionActualAcambiarDevice;
	int * nuevaPoblacionAcambiarDevice;
	float * mejorIndividuoGPUDevice;
	float * mejorFitnessIndividuoGPUDevice;

	// alocar en el device
	cudaMalloc(&poblacionActualDevice, tamVectorPoblacion);
	cudaMalloc(&nuevaPoblacionDevice, tamVectorPoblacion);
	cudaMalloc(&fitnessActualDevice, tamVectorFitnessFloat);
	cudaMalloc(&nuevoFitnessDevice, tamVectorFitnessFloat);
	cudaMalloc(&indicesFitnessOrdenadoDevice, tamVectorFitnessInt);
	cudaMalloc(&fitnessOrdenadoDevice, tamVectorFitnessFloat);
	cudaMalloc(&probabilidadRuletaDevice, tamVectorPoblacionParaRuleta);
	cudaMalloc(&fitnessTotalParaRuletaDevice, tamVectorFitnessRuleta);
	cudaMalloc(&poblacionActualAcambiarDevice, tamVectorFitnessInt);
	cudaMalloc(&nuevaPoblacionAcambiarDevice, tamVectorFitnessInt);
	cudaMalloc(&individuosAMigrarDevice, tamVectorIndividuosAMigrarGPU);
	cudaMalloc(&mejorIndividuoGPUDevice, tamVectorMejoresIndividuosGPU);
	cudaMalloc(&mejorFitnessIndividuoGPUDevice, tamVectorFitnessMejoresIndividuosGPU);

	//Inicio numeros aleatorios.
	int cantHilosRandom = 0;
	if(cantidadPoblacionesGPU > 0){
		cantHilosRandom = cantCromosomasPoblacion*cantidadPoblacionesGPU;
	}else{
		cantHilosRandom = cantCromosomasPoblacion*cantidadPoblacionesCPU;
	}
	struct Random r = initRandom(cantHilosRandom);

	//inicializo grid con dimension 1 x cantidadPoblacionesGPU*cantCromosomasPoblacion/2 bloques
	dim3 tamGrid(1, cantidadPoblacionesGPU*cantCromosomasPoblacion/2); //Grid dimensión
	//inicializo bloque con dimension 1 x cantGenesEnCromosoma*2 x 1 hilos
	dim3 tamBlock(1, cantHilosBloque, 1); //Block dimensión

	//inicializo grid de individuos a migrar con dimension 1 x cantidadPoblacionesGPU bloques
	dim3 tamGridIndividuosAMigrar(1, cantidadPoblacionesGPU);
	//inicializo bloque de individuos a migrar con dimension 1 x cantCromosomasPoblacion/2 x 1 hilos
	dim3 tamBlockIndividuosAMigrar(1,cantCromosomasPoblacion/2,1);

	//inicializo grid de migracion con dimension 1 x cantidadPoblacionesGPU bloques
	dim3 tamGridMigracion(1, cantidadPoblacionesGPU);
	//inicializo bloque de migracion con 1 hilo por bloque
	dim3 tamBlockMigracion(1,1,1); //Block dimensión

	/*******************************  *
	 *  Arreglos de nros aleatorios   *
	 *  *******************************/
	int tamVectorNumerosAleatoriosInicializacion;
	int tamVectorNumerosAleatoriosSeleccion;
	int tamVectorNumerosAleatoriosCruzamiento;
	int tamVectorNumerosAleatoriosMutacion;
	int tamVectorNumerosAleatoriosProbabilidadCruzamiento;
	int tamVectorNumerosAleatoriosProbabilidadMutacion;

	float* numerosAleatoriosInicializacion;
	unsigned long* numerosAleatoriosSeleccion;
	unsigned long* numerosAleatoriosCruzamiento;
	unsigned long* numerosAleatoriosMutacion;
	unsigned long* numerosAleatoriosProbabilidadCruzamiento;
	unsigned long* numerosAleatoriosProbabilidadMutacion;

	float* numerosAleatoriosInicializacionDevice;
	unsigned long* numerosAleatoriosSeleccionDevice;
	unsigned long* numerosAleatoriosCruzamientoDevice;
	unsigned long* numerosAleatoriosMutacionDevice;
	unsigned long* numerosAleatoriosProbabilidadCruzamientoDevice;
	unsigned long* numerosAleatoriosProbabilidadMutacionDevice;

	//inicializo la clase mt para las variables aleatorias
	MersenneTwister* mt;
	mt = new MersenneTwister();

	unsigned long init[4] = {0x123, 0x234, 0x345, 0x456}, length=4;
	mt->init_by_array(init, length);

	tamVectorNumerosAleatoriosInicializacion = sizeof(float)*(totalGenesGPU + totalGenesCPU);

	//Si la selección es por rango la cantidad de nros aleatorios es el doble que el nro de individuos
	if(tipoSeleccion == 1){
		tamVectorNumerosAleatoriosSeleccion = sizeof(unsigned long)*(totalIndividuosCPU + totalIndividuosGPU)*2;
	}else{
		tamVectorNumerosAleatoriosSeleccion = sizeof(unsigned long)*(totalIndividuosCPU + totalIndividuosGPU);
	}

	//Si el cruzamiento es aritmético individual la cantidad de nros aleatorios es igual al nro de individuos/2
	if(tipoCruzamiento == 0){
		tamVectorNumerosAleatoriosCruzamiento = sizeof(unsigned long)*(totalIndividuosCPU + totalIndividuosGPU);
		tamVectorNumerosAleatoriosProbabilidadCruzamiento = sizeof(unsigned long)*((totalIndividuosCPU + totalIndividuosGPU)/2);
	}else{
		//Si el cruzamiento es aritmético simple, la cantidad de nros aleatorios es igual al nro de individuos
		if(tipoCruzamiento == 1){
			tamVectorNumerosAleatoriosCruzamiento = sizeof(unsigned long)*(totalIndividuosCPU + totalIndividuosGPU);
			tamVectorNumerosAleatoriosProbabilidadCruzamiento = sizeof(unsigned long)*((totalIndividuosCPU + totalIndividuosGPU)/2);
		}else{ //Si aritmético completo, la cantidad de nros aleatorios es igual al nro de individuos
			if(tipoCruzamiento == 2){
				tamVectorNumerosAleatoriosCruzamiento = sizeof(unsigned long)*((totalIndividuosCPU + totalIndividuosGPU)/2);
				tamVectorNumerosAleatoriosProbabilidadCruzamiento = sizeof(unsigned long)*((totalIndividuosCPU + totalIndividuosGPU)/2);
			}
		}
	}
	if(tipoMutacion == 0){
		tamVectorNumerosAleatoriosMutacion = sizeof(unsigned long)*(totalIndividuosCPU + totalIndividuosGPU)*2;
	}else{
		tamVectorNumerosAleatoriosMutacion = sizeof(unsigned long)*(totalIndividuosCPU + totalIndividuosGPU)*3;
	}
	tamVectorNumerosAleatoriosProbabilidadMutacion = sizeof(unsigned long)*(totalIndividuosCPU + totalIndividuosGPU);

	numerosAleatoriosInicializacion = (float *) malloc(tamVectorNumerosAleatoriosInicializacion);
	numerosAleatoriosSeleccion = (unsigned long *) malloc(tamVectorNumerosAleatoriosSeleccion);
	numerosAleatoriosCruzamiento = (unsigned long *) malloc(tamVectorNumerosAleatoriosCruzamiento);
	numerosAleatoriosMutacion = (unsigned long *) malloc(tamVectorNumerosAleatoriosMutacion);
	numerosAleatoriosProbabilidadCruzamiento = (unsigned long *) malloc(tamVectorNumerosAleatoriosProbabilidadCruzamiento);
	numerosAleatoriosProbabilidadMutacion = (unsigned long *) malloc(tamVectorNumerosAleatoriosProbabilidadMutacion);

	generarNumerosAleatoriosReal(mt, numerosAleatoriosInicializacion, numerosAleatoriosSeleccion, numerosAleatoriosCruzamiento, numerosAleatoriosMutacion, numerosAleatoriosProbabilidadCruzamiento, numerosAleatoriosProbabilidadMutacion);

	cudaMalloc(&numerosAleatoriosInicializacionDevice, tamVectorNumerosAleatoriosInicializacion);
	cudaMalloc(&numerosAleatoriosSeleccionDevice, tamVectorNumerosAleatoriosSeleccion);
	cudaMalloc(&numerosAleatoriosCruzamientoDevice, tamVectorNumerosAleatoriosCruzamiento);
	cudaMalloc(&numerosAleatoriosMutacionDevice, tamVectorNumerosAleatoriosMutacion);
	cudaMalloc(&numerosAleatoriosProbabilidadCruzamientoDevice, tamVectorNumerosAleatoriosProbabilidadCruzamiento);
	cudaMalloc(&numerosAleatoriosProbabilidadMutacionDevice, tamVectorNumerosAleatoriosProbabilidadMutacion);

	cudaMemcpy(numerosAleatoriosInicializacionDevice, numerosAleatoriosInicializacion, tamVectorNumerosAleatoriosInicializacion, cudaMemcpyHostToDevice);
	cudaMemcpy(numerosAleatoriosSeleccionDevice, numerosAleatoriosSeleccion, tamVectorNumerosAleatoriosSeleccion, cudaMemcpyHostToDevice);
	cudaMemcpy(numerosAleatoriosCruzamientoDevice, numerosAleatoriosCruzamiento, tamVectorNumerosAleatoriosCruzamiento, cudaMemcpyHostToDevice);
	cudaMemcpy(numerosAleatoriosMutacionDevice, numerosAleatoriosMutacion, tamVectorNumerosAleatoriosMutacion, cudaMemcpyHostToDevice);
	cudaMemcpy(numerosAleatoriosProbabilidadCruzamientoDevice, numerosAleatoriosProbabilidadCruzamiento, tamVectorNumerosAleatoriosProbabilidadCruzamiento, cudaMemcpyHostToDevice);
	cudaMemcpy(numerosAleatoriosProbabilidadMutacionDevice, numerosAleatoriosProbabilidadMutacion, tamVectorNumerosAleatoriosProbabilidadMutacion, cudaMemcpyHostToDevice);
	
	//si el reemplazo para GPU es elitista entonces creo dim3 para el reemplazo que es cantAreemplazar bloques por poblacion
	int cantAremplazar = 1;
	int tamPosIndAreemplazarElitistaGPU;
	int *posIndAreemplazarViejosElitistaGPU;
	int *posIndAreemplazarNuevosElitistaGPU;
	if(cantidadPoblacionesGPU > 0){
		if(tipoReemplazo == 1){
			cantAremplazar = (int)(porcentajeElitismo * cantCromosomasPoblacion / 100);
			tamPosIndAreemplazarElitistaGPU = sizeof(int) * cantidadPoblacionesGPU * cantAremplazar;
			// alocar en el device
			cudaMalloc(&posIndAreemplazarViejosElitistaGPU, tamPosIndAreemplazarElitistaGPU);
			cudaMalloc(&posIndAreemplazarNuevosElitistaGPU, tamPosIndAreemplazarElitistaGPU);

		}
	}
	dim3 tamGridReemplazo(1, cantidadPoblacionesGPU * cantAremplazar);
	printf("%i\n",cantAremplazar);

	//inicializacion de la poblacion en GPU
	if(cantidadPoblacionesGPU > 0){
		if(inicializacionAutomatica == 1){
			inicializarPoblacionRealGen<<<tamGrid, tamBlock>>>(poblacionActualDevice, fitnessActualDevice, r, cantCromosomasPoblacion, fitnessOrdenadoDevice, numerosAleatoriosInicializacionDevice);
		}else{
			inicializarPoblacionRealGenManual<<<tamGrid, tamBlock>>>(poblacionActualDevice, fitnessActualDevice, r, cantCromosomasPoblacion, fitnessOrdenadoDevice, numerosAleatoriosInicializacionDevice);
		}
		asignarFitnessRealGen<<<tamGridMigracion, tamBlock>>>(poblacionActualDevice, fitnessActualDevice, fitnessOrdenadoDevice);
		obtenerMejoresIndividuoRealGen<<<tamGridMigracion, tamBlockMigracion>>>(poblacionActualDevice, fitnessActualDevice, mejorIndividuoGPUDevice, mejorFitnessIndividuoGPUDevice);
		//si la selección es por rango
		if(tipoSeleccion == 0){
			ordenarDescendente<<<tamGridMigracion, tamBlockMigracion>>>(fitnessOrdenadoDevice, indicesFitnessOrdenadoDevice);
		}
		
		if(imprimirPoblacionEntera == 1){
			traerImprimirRealGen(fitnessActualHostGPU, fitnessActualDevice, tamVectorFitnessFloat, poblacionActualHostGPU, poblacionActualDevice, tamVectorPoblacion, -1);
		}else{
			traerImprimirMejorIndividuoRealGen(mejorFitnessIndividuoGPU, mejorFitnessIndividuoGPUDevice, tamVectorFitnessMejoresIndividuosGPU, mejorIndividuoGPU, mejorIndividuoGPUDevice, tamVectorMejoresIndividuosGPU, -1);
		}
	}
	
	static float *individuosAMigrarHostCPU;
	int tamVectorPoblacionCPU;
	int tamVectorFitnessFloatCPU;
	int tamVectorFitnessIntCPU;
	static float *poblacionActualHostCPU;
	static float *nuevaPoblacionHostCPU;
	static float *fitnessActualHostCPU;
	static float *nuevoFitnessHostCPU;
	static int *indicesFitnessOrdenadoHostCPU;
	static float * fitnessOrdenadoHostCPU;
	int * poblacionActualAcambiarHostCPU;
	int * nuevaPoblacionAcambiarHostCPU;
	float * probabilidadRuletaHostCPU;
	float * fitnessTotalParaRuletaHostCPU;

	if(cantidadPoblacionesCPU > 0){

		tamVectorPoblacionCPU = sizeof(float)*totalGenesCPU;
		tamVectorFitnessFloatCPU = sizeof(float)*totalIndividuosCPU;
		tamVectorFitnessIntCPU = sizeof(int)*totalIndividuosCPU;

		poblacionActualHostCPU = (float *) malloc(tamVectorPoblacionCPU);
		nuevaPoblacionHostCPU = (float *) malloc(tamVectorPoblacionCPU);
		fitnessActualHostCPU = (float *) malloc(tamVectorFitnessFloatCPU);
		nuevoFitnessHostCPU = (float *) malloc(tamVectorFitnessFloatCPU);
		individuosAMigrarHostCPU = (float *) malloc(tamVectorIndividuosAMigrarCPU);
		indicesFitnessOrdenadoHostCPU = (int *) malloc(tamVectorFitnessIntCPU);
		fitnessOrdenadoHostCPU = (float *) malloc(tamVectorFitnessFloatCPU);
		poblacionActualAcambiarHostCPU = (int *) malloc(tamVectorFitnessIntCPU);
		nuevaPoblacionAcambiarHostCPU = (int *) malloc(tamVectorFitnessIntCPU);
		probabilidadRuletaHostCPU = (float *) malloc(tamVectorPoblacionParaRuletaCPU);
		fitnessTotalParaRuletaHostCPU = (float *) malloc(tamVectorFitnessRuletaCPU);

		//////////INICIALIZO POBLACION EN CPU
		if(inicializacionAutomatica == 1){
			inicializarPoblacionRealCPU(poblacionActualHostCPU, fitnessActualHostCPU, mt, cantCromosomasPoblacion, fitnessOrdenadoHostCPU, numerosAleatoriosInicializacion);
		}else{
			inicializarPoblacionRealCPUManual(poblacionActualHostCPU, fitnessActualHostCPU, mt, cantCromosomasPoblacion, fitnessOrdenadoHostCPU, numerosAleatoriosInicializacion);
		}
		if(modoDebug == 1){
			if(imprimirPoblacionEntera == 1)
				imprimirRealCPU(fitnessActualHostCPU, tamVectorFitnessFloatCPU, poblacionActualHostCPU, tamVectorPoblacionCPU, -1);
			else{
				obtenerMejoresIndividuoRealCPU(poblacionActualHostCPU,fitnessActualHostCPU,mejorIndividuoCPU,mejorFitnessIndividuoCPU);
				imprimirMejorIndividuoRealCPU(mejorFitnessIndividuoCPU, tamVectorFitnessMejoresIndividuosCPU, mejorIndividuoCPU, tamVectorMejoresIndividuosCPU, -1);
			}
		}

		//si la selección es por rango
		if(tipoSeleccion == 0){
			for(int i = 0; i < cantidadPoblacionesCPU; i++){
				ordenarDescendenteCPU(fitnessOrdenadoHostCPU, indicesFitnessOrdenadoHostCPU, i);
			}
		}
	}

	int tamVectorIndividuoAMigrarCPUGPU;
	static float *individuoAMigrarCPUaGPUhost;
	static float *individuoAMigrarGPUaCPUhost;
	static float *individuoAMigrarCPUaGPUdevice;
	static float *individuoAMigrarGPUaCPUdevice;
	if(cantidadPoblacionesCPU > 0 && cantidadPoblacionesGPU > 0){
		tamVectorIndividuoAMigrarCPUGPU = sizeof(float)*cantGenesEnCromosoma;
		cudaMalloc(&individuoAMigrarCPUaGPUdevice, tamVectorIndividuoAMigrarCPUGPU);
		cudaMalloc(&individuoAMigrarGPUaCPUdevice, tamVectorIndividuoAMigrarCPUGPU);
		individuoAMigrarCPUaGPUhost = (float *) malloc(tamVectorIndividuoAMigrarCPUGPU);
		individuoAMigrarGPUaCPUhost = (float *) malloc(tamVectorIndividuoAMigrarCPUGPU);
	}

	int iteracion = 0;
	//los individuos de la poblacion actual se van intercambiando en el reemplazo
	while(iteracion < numeroGeneraciones){
		if(cantidadPoblacionesGPU > 0){
			//si la iteracion es par invoco a GPU con poblacionActual, nuevaPoblacion
			if((iteracion % 2) == 0){
				if(tipoSeleccion == 2){
					asignarProbabilidadRuletaRealGen<<<tamGrid, tamBlock>>>(fitnessActualDevice, probabilidadRuletaDevice, fitnessTotalParaRuletaDevice, r);
				}
				// seleccionamos, cruzamos y mutamos en GPU
				mainKernelRealGen<<<tamGrid, tamBlock>>>(poblacionActualDevice, nuevaPoblacionDevice, fitnessActualDevice, nuevoFitnessDevice, poblacionActualAcambiarDevice, nuevaPoblacionAcambiarDevice, individuosAMigrarDevice, probabilidadRuletaDevice, fitnessTotalParaRuletaDevice, r, iteracion, individuoAMigrarGPUaCPUdevice, fitnessOrdenadoDevice, indicesFitnessOrdenadoDevice, numerosAleatoriosSeleccionDevice, numerosAleatoriosCruzamientoDevice, numerosAleatoriosMutacionDevice, numerosAleatoriosProbabilidadCruzamientoDevice, numerosAleatoriosProbabilidadMutacionDevice);
				asignarFitnessRealGen<<<tamGridMigracion, tamBlock>>>(nuevaPoblacionDevice, nuevoFitnessDevice, fitnessOrdenadoDevice);
				if(tipoReemplazo == 1)
					reemplazoElitistaSeleccionIndividuosRealGen<<<tamGridMigracion, tamBlockMigracion>>>(poblacionActualDevice, nuevaPoblacionDevice, poblacionActualAcambiarDevice, nuevaPoblacionAcambiarDevice, fitnessActualDevice, nuevoFitnessDevice, posIndAreemplazarViejosElitistaGPU, posIndAreemplazarNuevosElitistaGPU);
				reemplazoRealGen<<<tamGridReemplazo, tamBlock>>>(poblacionActualDevice, nuevaPoblacionDevice, posIndAreemplazarViejosElitistaGPU, posIndAreemplazarNuevosElitistaGPU);
				//si la selección es por rango
				if(tipoSeleccion == 0){
					ordenarDescendente<<<tamGridMigracion, tamBlockMigracion>>>(fitnessOrdenadoDevice, indicesFitnessOrdenadoDevice);
				}
				if(modoDebug == 1){
					if(imprimirPoblacionEntera == 1)
						traerImprimirRealGen(nuevoFitnessHostGPU, nuevoFitnessDevice, tamVectorFitnessFloat, nuevaPoblacionHostGPU, nuevaPoblacionDevice, tamVectorPoblacion, iteracion);
					else{
						obtenerMejoresIndividuoRealGen<<<tamGridMigracion, tamBlockMigracion>>>(nuevaPoblacionDevice, nuevoFitnessDevice, mejorIndividuoGPUDevice, mejorFitnessIndividuoGPUDevice);
						traerImprimirMejorIndividuoRealGen(mejorFitnessIndividuoGPU, mejorFitnessIndividuoGPUDevice, tamVectorFitnessMejoresIndividuosGPU, mejorIndividuoGPU, mejorIndividuoGPUDevice, tamVectorMejoresIndividuosGPU, iteracion);
					}
				}
			}else{//sino invoco a GPU con nuevaPoblacion, poblacionActual
				if(tipoSeleccion == 2){
					asignarProbabilidadRuletaRealGen<<<tamGrid, tamBlock>>>(nuevoFitnessDevice, probabilidadRuletaDevice, fitnessTotalParaRuletaDevice, r);
				}
				// seleccionamos, cruzamos y mutamos en GPU
				mainKernelRealGen<<<tamGrid, tamBlock>>>(nuevaPoblacionDevice, poblacionActualDevice, nuevoFitnessDevice, fitnessActualDevice, poblacionActualAcambiarDevice, nuevaPoblacionAcambiarDevice, individuosAMigrarDevice, probabilidadRuletaDevice, fitnessTotalParaRuletaDevice, r, iteracion, individuoAMigrarGPUaCPUdevice, fitnessOrdenadoDevice, indicesFitnessOrdenadoDevice, numerosAleatoriosSeleccionDevice, numerosAleatoriosCruzamientoDevice, numerosAleatoriosMutacionDevice, numerosAleatoriosProbabilidadCruzamientoDevice, numerosAleatoriosProbabilidadMutacionDevice);
				asignarFitnessRealGen<<<tamGridMigracion, tamBlock>>>(poblacionActualDevice, fitnessActualDevice, fitnessOrdenadoDevice);
				if(tipoReemplazo == 1)
					reemplazoElitistaSeleccionIndividuosRealGen<<<tamGridMigracion, tamBlockMigracion>>>(nuevaPoblacionDevice, poblacionActualDevice, poblacionActualAcambiarDevice, nuevaPoblacionAcambiarDevice, nuevoFitnessDevice, fitnessActualDevice, posIndAreemplazarViejosElitistaGPU, posIndAreemplazarNuevosElitistaGPU);
				reemplazoRealGen<<<tamGridReemplazo, tamBlock>>>(nuevaPoblacionDevice, poblacionActualDevice, posIndAreemplazarViejosElitistaGPU, posIndAreemplazarNuevosElitistaGPU);
				//si la selección es por rango
				if(tipoSeleccion == 0){
					ordenarDescendente<<<tamGridMigracion, tamBlockMigracion>>>(fitnessOrdenadoDevice, indicesFitnessOrdenadoDevice);
				}
				if(modoDebug == 1){
					if(imprimirPoblacionEntera == 1)
						traerImprimirRealGen(fitnessActualHostGPU, fitnessActualDevice, tamVectorFitnessFloat, poblacionActualHostGPU, poblacionActualDevice, tamVectorPoblacion, iteracion);
					else{
						obtenerMejoresIndividuoRealGen<<<tamGridMigracion, tamBlockMigracion>>>(poblacionActualDevice, fitnessActualDevice, mejorIndividuoGPUDevice, mejorFitnessIndividuoGPUDevice);
						traerImprimirMejorIndividuoRealGen(mejorFitnessIndividuoGPU, mejorFitnessIndividuoGPUDevice, tamVectorFitnessMejoresIndividuosGPU, mejorIndividuoGPU, mejorIndividuoGPUDevice, tamVectorMejoresIndividuosGPU, iteracion);
					}
				}
			}
		}
		//Tiro islas en CPU
		if(cantidadPoblacionesCPU > 0){
			representacionRealCPU(poblacionActualHostCPU, nuevaPoblacionHostCPU, fitnessActualHostCPU, nuevoFitnessHostCPU, poblacionActualAcambiarHostCPU, nuevaPoblacionAcambiarHostCPU, individuosAMigrarHostCPU, mt, iteracion, tamVectorPoblacion, tamVectorFitnessFloat, individuoAMigrarCPUaGPUhost, fitnessOrdenadoHostCPU, indicesFitnessOrdenadoHostCPU, probabilidadRuletaHostCPU, fitnessTotalParaRuletaHostCPU, numerosAleatoriosSeleccion, numerosAleatoriosCruzamiento, numerosAleatoriosMutacion, numerosAleatoriosProbabilidadCruzamiento, numerosAleatoriosProbabilidadMutacion, mejorFitnessIndividuoCPU, tamVectorFitnessMejoresIndividuosCPU, mejorIndividuoCPU, tamVectorMejoresIndividuosCPU);
			//si la selección es por rango
			if(tipoSeleccion == 0){
				for(int i = 0; i < cantidadPoblacionesCPU; i++){
					ordenarDescendenteCPU(fitnessOrdenadoHostCPU, indicesFitnessOrdenadoHostCPU, i);
				}
			}
		}
		generarNumerosAleatoriosReal(mt, numerosAleatoriosInicializacion, numerosAleatoriosSeleccion, numerosAleatoriosCruzamiento, numerosAleatoriosMutacion, numerosAleatoriosProbabilidadCruzamiento, numerosAleatoriosProbabilidadMutacion);
		cudaMemcpy(numerosAleatoriosInicializacionDevice, numerosAleatoriosInicializacion, tamVectorNumerosAleatoriosInicializacion, cudaMemcpyHostToDevice);
		cudaMemcpy(numerosAleatoriosSeleccionDevice, numerosAleatoriosSeleccion, tamVectorNumerosAleatoriosSeleccion, cudaMemcpyHostToDevice);
		cudaMemcpy(numerosAleatoriosCruzamientoDevice, numerosAleatoriosCruzamiento, tamVectorNumerosAleatoriosCruzamiento, cudaMemcpyHostToDevice);
		cudaMemcpy(numerosAleatoriosMutacionDevice, numerosAleatoriosMutacion, tamVectorNumerosAleatoriosMutacion, cudaMemcpyHostToDevice);
		cudaMemcpy(numerosAleatoriosProbabilidadCruzamientoDevice, numerosAleatoriosProbabilidadCruzamiento, tamVectorNumerosAleatoriosProbabilidadCruzamiento, cudaMemcpyHostToDevice);
		cudaMemcpy(numerosAleatoriosProbabilidadMutacionDevice, numerosAleatoriosProbabilidadMutacion, tamVectorNumerosAleatoriosProbabilidadMutacion, cudaMemcpyHostToDevice);
		//Si hay que migrar
		if((generacionesMigrar > 0) && ((iteracion + 1) % generacionesMigrar == 0)){

			if(cantidadPoblacionesGPU > 0){
				if((iteracion % 2) == 0){
					individosAMigrarRepresentacionRealGen<<<tamGridIndividuosAMigrar, tamBlockIndividuosAMigrar>>>(nuevoFitnessDevice, nuevaPoblacionDevice, individuosAMigrarDevice, individuoAMigrarGPUaCPUdevice);
				}else{
					individosAMigrarRepresentacionRealGen<<<tamGridIndividuosAMigrar, tamBlockIndividuosAMigrar>>>(fitnessActualDevice, poblacionActualDevice, individuosAMigrarDevice, individuoAMigrarGPUaCPUdevice);
				}
			}

			if(cantidadPoblacionesGPU > 0 && cantidadPoblacionesCPU > 0){
				cudaMemcpy(individuoAMigrarCPUaGPUdevice, individuoAMigrarCPUaGPUhost, tamVectorIndividuoAMigrarCPUGPU, cudaMemcpyHostToDevice);
				cudaMemcpy(individuoAMigrarGPUaCPUhost, individuoAMigrarGPUaCPUdevice, tamVectorIndividuoAMigrarCPUGPU, cudaMemcpyDeviceToHost);
				if(modoDebug == 1){
					/**  Imprimir individuos a migrar **/
					cudaMemcpy(individuosAMigrarHostGPU, individuosAMigrarDevice, tamVectorIndividuosAMigrarGPU, cudaMemcpyDeviceToHost);

					printf("----individuosAMigrar GPU----\n");
					for(int i = 0; i<cantGenesEnCromosoma*cantidadPoblacionesGPU; i++){
						printf("%f ", individuosAMigrarHostGPU[i]);
						if((i+1)%cantGenesEnCromosoma == 0){
							printf("|");
						}
					}
					printf("\n---------------------------\n");


					printf("----individuo a migrar de GPU a CPU----\n");
					for(int i = 0; i<cantGenesEnCromosoma; i++){
						printf("%f ", individuoAMigrarGPUaCPUhost[i]);
					}
					printf("\n-------------------------------------\n");

					printf("________individuosAMigrar CPU______\n");
					for(int i = 0; i<cantGenesEnCromosoma*cantidadPoblacionesCPU; i++){
						printf("%f ", individuosAMigrarHostCPU[i]);
						if((i+1)%cantGenesEnCromosoma == 0){
							printf("|");
						}
					}
					printf("\n__________________________________\n");

					printf("_____individuo a migrar de CPU a GPU_____\n");
					for(int i = 0; i<cantGenesEnCromosoma; i++){
						printf("%f ", individuoAMigrarCPUaGPUhost[i]);
					}
					printf("\n________________________________________\n");

					/** ************* **/
				}
				if((iteracion % 2) == 0){
					migracionRepresentacionRealGen<<<tamGridMigracion, tamBlockMigracion>>>(nuevaPoblacionDevice, nuevoFitnessDevice, individuosAMigrarDevice, r, individuoAMigrarCPUaGPUdevice);
					if(modoDebug == 1){
						cout <<"\n**Despues de Migrar GPU**";
						if(imprimirPoblacionEntera == 1)
							traerImprimirRealGen(nuevoFitnessHostGPU, nuevoFitnessDevice, tamVectorFitnessFloat, nuevaPoblacionHostGPU, nuevaPoblacionDevice, tamVectorPoblacion, iteracion);
						else{
							obtenerMejoresIndividuoRealGen<<<tamGridMigracion, tamBlockMigracion>>>(nuevaPoblacionDevice, nuevoFitnessDevice, mejorIndividuoGPUDevice, mejorFitnessIndividuoGPUDevice);
							traerImprimirMejorIndividuoRealGen(mejorFitnessIndividuoGPU, mejorFitnessIndividuoGPUDevice, tamVectorFitnessMejoresIndividuosGPU, mejorIndividuoGPU, mejorIndividuoGPUDevice, tamVectorMejoresIndividuosGPU, iteracion);
						}
					}
					migracionRepresentacionRealCPU(nuevaPoblacionHostCPU, nuevoFitnessHostCPU, individuosAMigrarHostCPU, mt, individuoAMigrarGPUaCPUhost);
					if(modoDebug == 1){
						cout <<"\n** Despues de Migrar CPU **";
						if(imprimirPoblacionEntera == 1)
							imprimirRealCPU(nuevoFitnessHostCPU, tamVectorFitnessFloat, nuevaPoblacionHostCPU, tamVectorPoblacion, iteracion);
						else
							imprimirMejorIndividuoRealCPU(mejorFitnessIndividuoCPU, tamVectorFitnessMejoresIndividuosCPU, mejorIndividuoCPU, tamVectorMejoresIndividuosCPU, iteracion);
					}
				}else{
					migracionRepresentacionRealGen<<<tamGridMigracion, tamBlockMigracion>>>(poblacionActualDevice, fitnessActualDevice, individuosAMigrarDevice, r, individuoAMigrarCPUaGPUdevice);
					if(modoDebug == 1){
						cout <<"\n**Despues de Migrar GPU**";
						if(imprimirPoblacionEntera == 1)
							traerImprimirRealGen(fitnessActualHostGPU, fitnessActualDevice, tamVectorFitnessFloat, poblacionActualHostGPU, poblacionActualDevice, tamVectorPoblacion, iteracion);
						else{
							obtenerMejoresIndividuoRealGen<<<tamGridMigracion, tamBlockMigracion>>>(poblacionActualDevice, fitnessActualDevice, mejorIndividuoGPUDevice, mejorFitnessIndividuoGPUDevice);
							traerImprimirMejorIndividuoRealGen(mejorFitnessIndividuoGPU, mejorFitnessIndividuoGPUDevice, tamVectorFitnessMejoresIndividuosGPU, mejorIndividuoGPU, mejorIndividuoGPUDevice, tamVectorMejoresIndividuosGPU, iteracion);
						}
					}
					migracionRepresentacionRealCPU(poblacionActualHostCPU, fitnessActualHostCPU, individuosAMigrarHostCPU, mt, individuoAMigrarGPUaCPUhost);
					if(modoDebug == 1){
						cout <<"\n** Despues de Migrar CPU **";
						if(imprimirPoblacionEntera == 1)
							imprimirRealCPU(fitnessActualHostCPU, tamVectorFitnessFloat, poblacionActualHostCPU, tamVectorPoblacion, iteracion);
						else
							imprimirMejorIndividuoRealCPU(mejorFitnessIndividuoCPU, tamVectorFitnessMejoresIndividuosCPU, mejorIndividuoCPU, tamVectorMejoresIndividuosCPU, iteracion);
					}
				}
			}else if(cantidadPoblacionesCPU > 0){
				//poblaciones solamente en CPU
				if((iteracion % 2) == 0){
					migracionRepresentacionRealCPU(nuevaPoblacionHostCPU, nuevoFitnessHostCPU, individuosAMigrarHostCPU, mt, individuoAMigrarGPUaCPUhost);
					if(modoDebug == 1){
						cout <<"\n** Despues de Migrar CPU **";
						if(imprimirPoblacionEntera == 1)
							imprimirRealCPU(nuevoFitnessHostCPU, tamVectorFitnessFloat, nuevaPoblacionHostCPU, tamVectorPoblacion, iteracion);
						else
							imprimirMejorIndividuoRealCPU(mejorFitnessIndividuoCPU, tamVectorFitnessMejoresIndividuosCPU, mejorIndividuoCPU, tamVectorMejoresIndividuosCPU, iteracion);
					}
				}else{
					migracionRepresentacionRealCPU(poblacionActualHostCPU, fitnessActualHostCPU, individuosAMigrarHostCPU, mt, individuoAMigrarGPUaCPUhost);
					if(modoDebug == 1){
						cout <<"\n** Despues de Migrar CPU **";
						if(imprimirPoblacionEntera == 1)
							imprimirRealCPU(fitnessActualHostCPU, tamVectorFitnessFloat, poblacionActualHostCPU, tamVectorPoblacion, iteracion);
						else
							imprimirMejorIndividuoRealCPU(mejorFitnessIndividuoCPU, tamVectorFitnessMejoresIndividuosCPU, mejorIndividuoCPU, tamVectorMejoresIndividuosCPU, iteracion);
					}
				}
				if(modoDebug == 1){
					printf("individuosAMigrar CPU\n");
					for(int i = 0; i<cantGenesEnCromosoma*cantidadPoblacionesCPU; i++){
						printf("%f ", individuosAMigrarHostCPU[i]);
						if((i+1)%cantGenesEnCromosoma == 0){
							printf("|");
						}
					}
					printf("\n-----------------\n");
				}
			}else{
				//poblaciones solamente en GPU
				if(modoDebug == 1){
					/**  Imprimir individuos a migrar **/
					cudaMemcpy(individuosAMigrarHostGPU, individuosAMigrarDevice, tamVectorIndividuosAMigrarGPU, cudaMemcpyDeviceToHost);

					printf("individuosAMigrar GPU\n");
					for(int i = 0; i<cantGenesEnCromosoma*cantidadPoblacionesGPU; i++){
						printf("%f ", individuosAMigrarHostGPU[i]);
						if((i+1)%cantGenesEnCromosoma == 0){
							printf("|");
						}
					}
					printf("\n-----------------\n");
					/** ************* **/
				}
				if((iteracion % 2) == 0){
					migracionRepresentacionRealGen<<<tamGridMigracion, tamBlockMigracion>>>(nuevaPoblacionDevice, nuevoFitnessDevice, individuosAMigrarDevice, r, individuoAMigrarCPUaGPUdevice);
					if(modoDebug == 1){
						cout <<"\n** Despues de Migrar **";
						if(imprimirPoblacionEntera == 1)
							traerImprimirRealGen(nuevoFitnessHostGPU, nuevoFitnessDevice, tamVectorFitnessFloat, nuevaPoblacionHostGPU, nuevaPoblacionDevice, tamVectorPoblacion, iteracion);
						else{
							obtenerMejoresIndividuoRealGen<<<tamGridMigracion, tamBlockMigracion>>>(nuevaPoblacionDevice, nuevoFitnessDevice, mejorIndividuoGPUDevice, mejorFitnessIndividuoGPUDevice);
							traerImprimirMejorIndividuoRealGen(mejorFitnessIndividuoGPU, mejorFitnessIndividuoGPUDevice, tamVectorFitnessMejoresIndividuosGPU, mejorIndividuoGPU, mejorIndividuoGPUDevice, tamVectorMejoresIndividuosGPU, iteracion);
						}
					}
				}else{
					migracionRepresentacionRealGen<<<tamGridMigracion, tamBlockMigracion>>>(poblacionActualDevice, fitnessActualDevice, individuosAMigrarDevice, r, individuoAMigrarCPUaGPUdevice);
					if(modoDebug == 1){
						cout <<"\n** Despues de Migrar **";
						if(imprimirPoblacionEntera == 1)
							traerImprimirRealGen(fitnessActualHostGPU, fitnessActualDevice, tamVectorFitnessFloat, poblacionActualHostGPU, poblacionActualDevice, tamVectorPoblacion, iteracion);
						else{
							obtenerMejoresIndividuoRealGen<<<tamGridMigracion, tamBlockMigracion>>>(poblacionActualDevice, fitnessActualDevice, mejorIndividuoGPUDevice, mejorFitnessIndividuoGPUDevice);
							traerImprimirMejorIndividuoRealGen(mejorFitnessIndividuoGPU, mejorFitnessIndividuoGPUDevice, tamVectorFitnessMejoresIndividuosGPU, mejorIndividuoGPU, mejorIndividuoGPUDevice, tamVectorMejoresIndividuosGPU, iteracion);
						}
					}
				}
			}
		}
		//mejorFitness = obtenerMejorFitness(poblacionActual,mejorCromosoma);
		iteracion++;
	}

	if(cantidadPoblacionesGPU > 0){
		if(((iteracion -1) % 2) == 0){
			if(imprimirPoblacionEntera == 1)
				traerImprimirRealGen(nuevoFitnessHostGPU, nuevoFitnessDevice, tamVectorFitnessFloat, nuevaPoblacionHostGPU, nuevaPoblacionDevice, tamVectorPoblacion, iteracion);
			else{
				obtenerMejoresIndividuoRealGen<<<tamGridMigracion, tamBlockMigracion>>>(nuevaPoblacionDevice, nuevoFitnessDevice, mejorIndividuoGPUDevice, mejorFitnessIndividuoGPUDevice);
				traerImprimirMejorIndividuoRealGen(mejorFitnessIndividuoGPU, mejorFitnessIndividuoGPUDevice, tamVectorFitnessMejoresIndividuosGPU, mejorIndividuoGPU, mejorIndividuoGPUDevice, tamVectorMejoresIndividuosGPU, iteracion);
			}
		}else{
			if(imprimirPoblacionEntera == 1)
				traerImprimirRealGen(fitnessActualHostGPU, fitnessActualDevice, tamVectorFitnessFloat, poblacionActualHostGPU, poblacionActualDevice, tamVectorPoblacion, iteracion);
			else{
				obtenerMejoresIndividuoRealGen<<<tamGridMigracion, tamBlockMigracion>>>(poblacionActualDevice, fitnessActualDevice, mejorIndividuoGPUDevice, mejorFitnessIndividuoGPUDevice);
				traerImprimirMejorIndividuoRealGen(mejorFitnessIndividuoGPU, mejorFitnessIndividuoGPUDevice, tamVectorFitnessMejoresIndividuosGPU, mejorIndividuoGPU, mejorIndividuoGPUDevice, tamVectorMejoresIndividuosGPU, iteracion);
			}
		}
	}

	// free memory for all Cuda malloc
	cudaFree(poblacionActualDevice);
	cudaFree(nuevaPoblacionDevice);
	cudaFree(fitnessActualDevice);
	cudaFree(fitnessOrdenadoDevice);
	cudaFree(indicesFitnessOrdenadoDevice);
	cudaFree(nuevoFitnessDevice);
	cudaFree(probabilidadRuletaDevice);
	cudaFree(fitnessTotalParaRuletaDevice);
	cudaFree(poblacionActualAcambiarDevice);
	cudaFree(nuevaPoblacionAcambiarDevice);
	cudaFree(individuosAMigrarDevice);
	cudaFree(mejorIndividuoGPUDevice);
	cudaFree(mejorFitnessIndividuoGPUDevice);
	cudaFree(numerosAleatoriosInicializacionDevice);
	cudaFree(numerosAleatoriosSeleccionDevice);
	cudaFree(numerosAleatoriosCruzamientoDevice);
	cudaFree(numerosAleatoriosMutacionDevice);
	cudaFree(numerosAleatoriosProbabilidadCruzamientoDevice);
	cudaFree(numerosAleatoriosProbabilidadMutacionDevice);

	// free memory for all C++ new
	delete mt;

	deleteRandom(r);

	// free memory for all C malloc
	free(poblacionActualHostGPU);
	free(nuevaPoblacionHostGPU);
	free(fitnessActualHostGPU);
	free(nuevoFitnessHostGPU);
	free(probabilidadRuletaHost);
	free(fitnessTotalParaRuletaHost);
	free(individuosAMigrarHostGPU);
	free(mejorIndividuoCPU);
	free(mejorIndividuoGPU);
	free(mejorFitnessIndividuoCPU);
	free(mejorFitnessIndividuoGPU);
	free(indicesFitnessOrdenadoHost);
	free(numerosAleatoriosInicializacion);
	free(numerosAleatoriosSeleccion);
	free(numerosAleatoriosCruzamiento);
	free(numerosAleatoriosMutacion);
	free(numerosAleatoriosProbabilidadCruzamiento);
	free(numerosAleatoriosProbabilidadMutacion);

	if(cantidadPoblacionesCPU > 0){
		free(poblacionActualHostCPU);
		free(nuevaPoblacionHostCPU);
		free(fitnessActualHostCPU);
		free(nuevoFitnessHostCPU);
		free(individuosAMigrarHostCPU);
		free(indicesFitnessOrdenadoHostCPU);
		free(fitnessOrdenadoHostCPU);
		free(poblacionActualAcambiarHostCPU);
		free(nuevaPoblacionAcambiarHostCPU);
		free(probabilidadRuletaHostCPU);
		free(fitnessTotalParaRuletaHostCPU);
	}

	if(cantidadPoblacionesCPU > 0 && cantidadPoblacionesGPU > 0){
		cudaFree(individuoAMigrarCPUaGPUdevice);
		cudaFree(individuoAMigrarGPUaCPUdevice);
		free(individuoAMigrarCPUaGPUhost);
		free(individuoAMigrarGPUaCPUhost);
	}

	printf("8\n");
	printf("%s\n", cudaGetErrorString(cudaPeekAtLastError()));
	printf("%s\n", cudaGetErrorString(cudaThreadSynchronize()));
	printf("8\n");
}

#endif
